using System;
using System.Collections.Generic;
using NUnit.Framework;

// ReSharper disable InconsistentNaming
namespace NFreeMarker.Tests.Template
{
    public class TemplateTests_ProcessBuiltInsForSequences
    {
        [TestFixture]
        public class When_processing_a_chunk_builtin : TemplateTestsBase
        {
            [Test]
            public void Assure_an_empty_list_remains_empty()
            {
                SetTemplateContent(
                    "<#list seq?chunk(2) as row>" +
                    "[<#list row as cell>${cell}</#list>]" +
                    "</#list>");
                AddTemplateVariable("seq", new string[0]);

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo(""));
            }

            [Test]
            public void Assure_an_empty_list_remains_empty_when_the_padding_value_is_specified()
            {
                SetTemplateContent(
                    "<#list seq?chunk(2, '-') as row>" +
                    "[<#list row as cell>${cell}</#list>]" +
                    "</#list>");
                AddTemplateVariable("seq", new string[0]);

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo(""));
            }

            [Test]
            public void Assure_a_list_larger_than_the_chunk_size_is_split_into_several_lists()
            {
                SetTemplateContent(
                    "<#list seq?chunk(2) as row>" +
                    "[<#list row as cell>${cell}</#list>]" +
                    "</#list>");
                AddTemplateVariable("seq", new [] { "A", "B", "C" });

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("[AB][C]"));
            }

            [Test]
            public void Assure_the_last_list_is_padded_to_match_the_chunk_size()
            {
                SetTemplateContent(
                    "<#list seq?chunk(2, '-') as row>" +
                    "[<#list row as cell>${cell}</#list>]" +
                    "</#list>");
                AddTemplateVariable("seq", new [] { "A", "B", "C" });

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("[AB][C-]"));
            }
        }

        [TestFixture]
        public class When_processing_a_first_builtin : TemplateTestsBase
        {
            [Test]
            public void Assure_the_first_element_is_returned()
            {
                SetTemplateContent("${list?first}");
                AddTemplateVariable("list", new[] { "Hello", "World" });

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("Hello"));
            }
        }

        [TestFixture]
        public class When_processing_a_last_builtin : TemplateTestsBase
        {
            [Test]
            public void Assure_the_first_element_is_returned()
            {
                SetTemplateContent("${list?last}");
                AddTemplateVariable("list", new[] { "Hello", "World" });

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("World"));
            }
        }

        [TestFixture]
        public class When_processing_a_reverse_builtin : TemplateTestsBase
        {
            [Test]
            public void Assure_the_list_is_outputted_in_reverse_order()
            {
                SetTemplateContent(
                    "<#list seq?reverse as x>" +
                    "${x}" +
                    "</#list>");
                AddTemplateVariable("seq", new[] { "A", "B", "C" });

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("CBA"));
            }
        }

        [TestFixture]
        public class When_processing_a_seq_contains_builtin : TemplateTestsBase
        {
            [Test]
            public void Assure_true_is_returned_when_the_sequence_contains_the_value()
            {
                SetTemplateContent("${list?seq_contains('Hello')?string}");
                AddTemplateVariable("list", new[] { "Hello", "World" });

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("true"));
            }

            [Test]
            public void Assure_false_is_returned_when_the_sequence_does_not_contain_the_value()
            {
                SetTemplateContent("${list?seq_contains('bogus')?string}");
                AddTemplateVariable("list", new[] { "Hello", "World" });

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("false"));
            }
        }

        [TestFixture]
        public class When_processing_a_seq_index_of_builtin : TemplateTestsBase
        {
            [Test]
            public void Assure_the_index_is_returned_when_the_value_exists()
            {
                SetTemplateContent("${list?seq_index_of('Hello')}");
                AddTemplateVariable("list", new[] { "Hello", "World", "Hello" });

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("0"));
            }

            [Test]
            public void Assure_the_second_index_is_returned_when_the_start_index_is_larger_than_the_first_index()
            {
                SetTemplateContent("${list?seq_index_of('Hello', 1)}");
                AddTemplateVariable("list", new[] { "Hello", "World", "Hello" });

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("2"));
            }

            [Test]
            public void Assure_minus_one_is_returned_when_the_value_does_not_exists()
            {
                SetTemplateContent("${list?seq_index_of('bogus')}");
                AddTemplateVariable("list", new[] { "Hello", "World" });

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("-1"));
            }
        }

        [TestFixture]
        public class When_processing_a_seq_last_index_of_builtin : TemplateTestsBase
        {
            [Test]
            public void Assure_the_index_is_returned_when_the_value_exists()
            {
                SetTemplateContent("${list?seq_last_index_of('Hello')}");
                AddTemplateVariable("list", new[] { "Hello", "World", "Hello" });

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("2"));
            }

            [Test]
            public void Assure_the_first_index_is_returned_when_the_start_index_is_smaller_than_the_second_index()
            {
                SetTemplateContent("${list?seq_last_index_of('Hello', 1)}");
                AddTemplateVariable("list", new[] { "Hello", "World", "Hello" });

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("0"));
            }

            [Test]
            public void Assure_minus_one_is_returned_when_the_value_does_not_exists()
            {
                SetTemplateContent("${list?seq_last_index_of('bogus')}");
                AddTemplateVariable("list", new[] { "Hello", "World" });

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("-1"));
            }
        }

        [TestFixture]
        public class When_processing_a_size_builtin : TemplateTestsBase
        {
            [Test]
            public void Assure_the_number_of_elements_is_returned()
            {
                SetTemplateContent("${list?size}");
                AddTemplateVariable("list", new[] { "A", "B", "C" });

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("3"));
            }

            [Test]
            public void Assure_zero_is_returned_for_an_empty_list()
            {
                SetTemplateContent("${list?size}");
                AddTemplateVariable("list", new string[0]);

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("0"));
            }
        }

        [TestFixture]
        public class When_processing_a_sort_builtin : TemplateTestsBase
        {
            [Test]
            public void Assure_the_list_of_dates_is_outputted_in_ascending_order()
            {
                SetTemplateContent(
                    "<#list seq?sort as x>" +
                    "[${x?date}]" +
                    "</#list>");
                AddTemplateVariable("seq", new[]
                {
                    new DateTimeOffset(2001, 1, 2, 0, 0, 0, TimeSpan.Zero),
                    new DateTimeOffset(2001, 2, 2, 0, 0, 0, TimeSpan.Zero),
                    new DateTimeOffset(2000, 1, 2, 0, 0, 0, TimeSpan.Zero)
                });

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo(
                    "[Sunday, January 02, 2000][Tuesday, January 02, 2001][Friday, February 02, 2001]"));
            }

            [Test]
            public void Assure_the_list_of_numbers_is_outputted_in_ascending_order()
            {
                SetTemplateContent(
                    "<#list seq?sort as x>" +
                    "[${x}]" +
                    "</#list>");
                AddTemplateVariable("seq", new[] { 3, 1, -1, 2 });

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("[-1][1][2][3]"));
            }

            [Test]
            public void Assure_the_list_of_strings_is_outputted_in_ascending_order()
            {
                SetTemplateContent(
                    "<#list seq?sort as x>" +
                    "[${x}]" +
                    "</#list>");
                AddTemplateVariable("seq", new[] { "AB", "B", "1", "C", "A" });

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("[1][A][AB][B][C]"));
            }

            [Test]
            public void Assure_an_empty_list_is_untouched()
            {
                SetTemplateContent(
                    "<#list seq?sort as x>" +
                    "[${x}]" +
                    "</#list>"); 
                AddTemplateVariable("seq", new string[0]);

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo(""));
            }
        }

        [TestFixture]
        public class When_processing_a_sort_by_builtin : TemplateTestsBase
        {
            [Test]
            public void Assure_the_sequence_is_sorted_by_the_string_subvariable()
            {
                SetTemplateContent(
                    "<#list seq?sort_by('value') as x>" +
                    "[${x.name}-${x.value}]" +
                    "</#list>");
                AddTemplateVariable("seq", new []
                {
                    new Dictionary<string, object>
                    {
                        { "name", "B" },
                        { "value", 2 }
                    },
                    new Dictionary<string, object>
                    {
                        { "name", "A" },
                        { "value", 3 }
                    },
                    new Dictionary<string, object>
                    {
                        { "name", "C" },
                        { "value", 1 }
                    }
                });

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("[C-1][B-2][A-3]"));
            }

            [Test]
            public void Assure_the_sequence_is_sorted_by_the_string_subsubvariable()
            {
                SetTemplateContent(
                    "<#list seq?sort_by(['name', 'last']) as x>" +
                    "[${x.name.first} ${x.name.last} - ${x.age}]" +
                    "</#list>");
                AddTemplateVariable("seq", new[]
                {
                    new Dictionary<string, object>
                    {
                        {
                            "name",
                            new Dictionary<string, string>
                            {
                                { "first", "Joe" },
                                { "last", "Smith" }
                            }
                        },
                        { "age", 2 }
                    },
                    new Dictionary<string, object>
                    {
                        {
                            "name",
                            new Dictionary<string, string>
                            {
                                { "first", "Amanda" },
                                { "last", "Fox" }
                            }
                        },
                        { "age", 3 }
                    }
                });

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("[Amanda Fox - 3][Joe Smith - 2]"));
            }

            [Test]
            public void Assure_the_sequence_is_sorted_by_the_number_subvariable()
            {
                SetTemplateContent(
                    "<#list seq?sort_by('name') as x>" +
                    "[${x.name}-${x.value}]" +
                    "</#list>");
                AddTemplateVariable("seq", new []
                {
                    new Dictionary<string, object>
                    {
                        { "name", "B" },
                        { "value", 2 }
                    },
                    new Dictionary<string, object>
                    {
                        { "name", "A" },
                        { "value", 3 }
                    },
                    new Dictionary<string, object>
                    {
                        { "name", "C" },
                        { "value", 1 }
                    }
                });

                string output = ProcessTemplate();

                Assert.That(output, Is.EqualTo("[A-3][B-2][C-1]"));
            }
        }
    }
}
// ReSharper enable InconsistentNaming
